#include <stdio.h>
#include <string.h>
#include <sys/param.h>
#include <sys/unistd.h>
#include <sys/stat.h>
#include <dirent.h>
#include "epd.h"
#include "epd_port.h"

#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "driver/spi_master.h"
#include "esp_err.h"
#include "esp_log.h"

#define PSR 0x00
#define PWRR 0x01
#define POF 0x02
#define POFS 0x03
#define PON 0x04
#define BTST1 0x05
#define BTST2 0x06
#define DSLP 0x07
#define BTST3 0x08
#define DTM 0x10
#define DRF 0x12
#define PLL 0x30
#define CDI 0x50
#define TCON 0x60
#define TRES 0x61
#define REV 0x70
#define VDCS 0x82
#define T_VDCS 0x84
#define PWS 0xE3

static const char *TAG = "EPD";

#define DELAY(ms) vTaskDelay(pdMS_TO_TICKS(ms))

#define EPD_RESET() epd_reset(handle)
#define EPD_W_CMD(cmd) epd_write_cmd(handle, cmd)
#define EPD_W_DATA(data) epd_write_byte(handle, data)
#define EPD_WAIT_BUSY() epd_wait_for_idle(handle)

uint8_t color_map(uint8_t color)
{
  switch (color)
  {
  case 0x00: // Black
    return EPD_BLACK;
  case 0xff: // White
    return EPD_WHITE;
  case 0xfc: // Yellow
    return EPD_YELLOW;
  case 0xE0: // Red
    return EPD_RED;
  case 0x03: // Blue
    return EPD_BLUE;
  case 0x1c: // Green
    return EPD_GREEN;
  default:
    return EPD_BLACK;
  }
  return EPD_BLACK;
}

esp_err_t epd_dis_pic(epd_handle_t *handle, const uint8_t *picData)
{
  handle->status = EPD_STS_BUSY;
  // Acep_color(White); //Each refresh must be cleaned first
  EPD_W_CMD(0x10);
  // 分块写入以适应缓冲区大小
  size_t total_pixels = handle->width * handle->height;
  size_t total_bytes = (total_pixels + 1) / 2; // 每个字节表示两个像素
  size_t offset = 0;
  size_t buf_size = handle->dis_buf_size;
  uint8_t *buf = handle->dis_buf;
  while (offset < total_bytes)
  {
    size_t chunk_size = buf_size < (total_bytes - offset) ? buf_size : (total_bytes - offset);
    // 填充缓冲区
    for (size_t i = 0; i < chunk_size; i++)
    {
      size_t pixel_index = offset + i;
      size_t byte_index = pixel_index * 2;
      uint8_t temp1 = picData[byte_index];
      uint8_t temp2 = (byte_index + 1 < total_pixels) ? picData[byte_index + 1] : 0xFF; // 防止越界
      buf[i] = color_map(temp1) << 4 | color_map(temp2);
    }
    // 写入数据
    epd_write_data(handle, buf, chunk_size);
    offset += chunk_size;
  }

  // Refresh
  EPD_W_CMD(0x12); // DISPLAY REFRESH
  EPD_W_DATA(0x00);
  DELAY(1);        //!!!The DELAY here is necessary, 200uS at least!!!
  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal
  handle->status = EPD_STS_READY;
  return ESP_OK;
}

esp_err_t epd_dis_e6_file(epd_handle_t *handle, const char *filename)
{

  FILE *file = fopen(filename, "rb");
  if (file == NULL)
  {
    ESP_LOGE(TAG, "Failed to open file: %s", filename);
    return ESP_FAIL;
  }
  handle->status = EPD_STS_BUSY;

  EPD_W_CMD(0x10);
  // 分块写入以适应缓冲区大小
  size_t total_pixels = handle->width * handle->height;
  size_t total_bytes = (total_pixels + 1) / 2; // 每个字节表示两个像素
  size_t offset = 0;
  size_t buf_size = handle->dis_buf_size;
  uint8_t *buf = handle->dis_buf;
  while (offset < total_bytes)
  {
    size_t chunk_size = buf_size < (total_bytes - offset) ? buf_size : (total_bytes - offset);
    size_t read_bytes = fread(buf, 1, chunk_size, file);
    if (read_bytes == 0)
    {
      ESP_LOGE(TAG, "Failed to read from file: %s", filename);
      fclose(file);
      handle->status = EPD_STS_READY;
      return ESP_FAIL;
    }
    // 写入数据
    epd_write_data(handle, buf, read_bytes);
    offset += read_bytes;
  }

  fclose(file);

  // Refresh
  EPD_W_CMD(0x12); // DISPLAY REFRESH
  EPD_W_DATA(0x00);
  DELAY(1);        //!!!The DELAY here is necessary, 200uS at least!!!
  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal
  handle->status = EPD_STS_READY;
  return ESP_OK;
}

esp_err_t epd_init(epd_handle_t *handle)
{
  handle->status = EPD_STS_BUSY;
  EPD_RESET(); // Electronic paper IC reset

  EPD_W_CMD(0xAA); // CMDH
  EPD_W_DATA(0x49);
  EPD_W_DATA(0x55);
  EPD_W_DATA(0x20);
  EPD_W_DATA(0x08);
  EPD_W_DATA(0x09);
  EPD_W_DATA(0x18);

  EPD_W_CMD(PWRR); //
  EPD_W_DATA(0x3F);

  EPD_W_CMD(PSR);
  EPD_W_DATA(0x5F);
  EPD_W_DATA(0x69);

  EPD_W_CMD(POFS);
  EPD_W_DATA(0x00);
  EPD_W_DATA(0x54);
  EPD_W_DATA(0x00);
  EPD_W_DATA(0x44);

  EPD_W_CMD(BTST1);
  EPD_W_DATA(0x40);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x2C);

  EPD_W_CMD(BTST2);
  EPD_W_DATA(0x6F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x17);
  EPD_W_DATA(0x49);

  EPD_W_CMD(BTST3);
  EPD_W_DATA(0x6F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x22);
  EPD_W_CMD(PLL);
  EPD_W_DATA(0x08);
  EPD_W_CMD(CDI);
  EPD_W_DATA(0x3F);

  EPD_W_CMD(TCON);
  EPD_W_DATA(0x02);
  EPD_W_DATA(0x00);

  EPD_W_CMD(TRES);
  EPD_W_DATA(0x03);
  EPD_W_DATA(0x20);
  EPD_W_DATA(0x01);
  EPD_W_DATA(0xE0);

  EPD_W_CMD(T_VDCS);
  EPD_W_DATA(0x01);

  EPD_W_CMD(PWS);
  EPD_W_DATA(0x2F);

  EPD_W_CMD(0x04); // PWR on

  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal

  handle->status = EPD_STS_READY;
  return ESP_OK;
}

// fast
esp_err_t epd_init_fast(epd_handle_t *handle)
{
  handle->status = EPD_STS_BUSY;
  EPD_RESET(); // Electronic paper IC reset

  EPD_W_CMD(0xAA); // CMDH
  EPD_W_DATA(0x49);
  EPD_W_DATA(0x55);
  EPD_W_DATA(0x20);
  EPD_W_DATA(0x08);
  EPD_W_DATA(0x09);
  EPD_W_DATA(0x18);

  EPD_W_CMD(PWRR);
  EPD_W_DATA(0x3F);
  EPD_W_DATA(0x00);
  EPD_W_DATA(0x32);
  EPD_W_DATA(0x2A);
  EPD_W_DATA(0x0E);
  EPD_W_DATA(0x2A);

  EPD_W_CMD(PSR);
  EPD_W_DATA(0x5F);
  EPD_W_DATA(0x69);

  EPD_W_CMD(POFS);
  EPD_W_DATA(0x00);
  EPD_W_DATA(0x54);
  EPD_W_DATA(0x00);
  EPD_W_DATA(0x44);

  EPD_W_CMD(BTST1);
  EPD_W_DATA(0x40);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x2C);

  EPD_W_CMD(BTST2);
  EPD_W_DATA(0x6F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x16);
  EPD_W_DATA(0x25);

  EPD_W_CMD(BTST3);
  EPD_W_DATA(0x6F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x22);

  EPD_W_CMD(0x13); // IPC
  EPD_W_DATA(0x00);
  EPD_W_DATA(0x04);

  EPD_W_CMD(PLL);
  EPD_W_DATA(0x02);

  EPD_W_CMD(0x41); // TSE
  EPD_W_DATA(0x00);

  EPD_W_CMD(CDI);
  EPD_W_DATA(0x3F);

  EPD_W_CMD(TCON);
  EPD_W_DATA(0x02);
  EPD_W_DATA(0x00);

  EPD_W_CMD(TRES);
  EPD_W_DATA(0x03);
  EPD_W_DATA(0x20);
  EPD_W_DATA(0x01);
  EPD_W_DATA(0xE0);

  EPD_W_CMD(VDCS);
  EPD_W_DATA(0x1E);

  EPD_W_CMD(T_VDCS);
  EPD_W_DATA(0x01);

  EPD_W_CMD(0x86); // AGID
  EPD_W_DATA(0x00);

  EPD_W_CMD(PWS);
  EPD_W_DATA(0x2F);

  EPD_W_CMD(0xE0); // CCSET
  EPD_W_DATA(0x00);

  EPD_W_CMD(0xE6); // TSSET
  EPD_W_DATA(0x00);

  EPD_W_CMD(0x04); // PWR on
  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal
  handle->status = EPD_STS_READY;
  return ESP_OK;
}

esp_err_t epd_sleep(epd_handle_t *handle)
{
  handle->status = EPD_STS_BUSY;
  EPD_W_CMD(POF);
  EPD_W_DATA(0x00);
  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal
  /*EPD_W_CMD(0X07);   //deep sleep
  EPD_W_DATA(0xA5);*/
  handle->status = EPD_STS_SLEEP;
  return ESP_OK;
}

esp_err_t epd_wake(epd_handle_t *handle)
{
  handle->status = EPD_STS_BUSY;
  EPD_W_CMD(PON);  // PWR on
  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal
  handle->status = EPD_STS_READY;
  return ESP_OK;
}

esp_err_t epd_flush(epd_handle_t *handle)
{
  handle->status = EPD_STS_BUSY;
  EPD_W_CMD(DRF); // DISPLAY REFRESH
  EPD_W_DATA(0x00);
  DELAY(1);        //!!!The DELAY here is necessary, 200uS at least!!!
  EPD_WAIT_BUSY(); // waiting for the electronic paper IC to release the idle signal
  handle->status = EPD_STS_READY;
  return ESP_OK;
}

esp_err_t epd_reset(epd_handle_t *handle)
{
  epd_port_set_reset(handle->port, 0);
  DELAY(10);
  epd_port_set_reset(handle->port, 1);
  DELAY(10);
  return ESP_OK;
}

esp_err_t epd_wait_for_idle(epd_handle_t *handle)
{
  while (epd_port_is_busy(handle->port))
  {
    DELAY(1);
  }
  return ESP_OK;
}

inline esp_err_t epd_write_cmd(epd_handle_t *handle, uint8_t cmd)
{
  return epd_port_write(handle->port, &cmd, 1, 0);
}

inline esp_err_t epd_write_byte(epd_handle_t *handle, uint8_t data)
{
  return epd_port_write(handle->port, &data, 1, 1);
}

inline esp_err_t epd_write_data(epd_handle_t *handle, uint8_t *data, size_t len)
{
  return epd_port_write(handle->port, data, len, 1);
}

esp_err_t epd_fill_screen(epd_handle_t *handle, uint8_t color)
{
  color = (color & 0x0F) | ((color & 0x0F) << 4); // 4bit to 8bit
  uint8_t *buf = handle->dis_buf;
  size_t buf_size = handle->dis_buf_size;
  size_t total_pixels = handle->width * handle->height;
  size_t total_bytes = (total_pixels + 1) / 2; // 每个字节表示两个像素
  size_t offset = 0;
  // 填充缓冲区
  memset(buf, color, buf_size);
  EPD_W_CMD(DTM);
  // 分块写入数据以适应缓冲区大小
  while (offset < total_bytes)
  {
    size_t chunk_size = buf_size < (total_bytes - offset) ? buf_size : (total_bytes - offset);
    epd_write_data(handle, buf, chunk_size);
    offset += chunk_size;
  }
  epd_wake(handle);

  // 20211212
  // Second setting
  EPD_W_CMD(BTST2);
  EPD_W_DATA(0x6F);
  EPD_W_DATA(0x1F);
  EPD_W_DATA(0x17);
  EPD_W_DATA(0x49);

  epd_flush(handle);

  return ESP_OK;
}

inline esp_err_t epd_clear(epd_handle_t *handle)
{
  return epd_fill_screen(handle, EPD_WHITE);
}
